﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Linq;
using System.Reflection;
using System.Linq.Expressions;
using System.Data.Linq.Mapping;


namespace VQWEB.common
{
    public class DataLayer<TContext> where TContext : DataContext, new()
    {
        TContext context = new TContext();

        private KeyValuePair<string, Type> GetPrimaryKey<T>() where T : class
        {

            string key = string.Empty;
            Type type = null;
            MetaTable userMeta = context.Mapping.GetTable(typeof(T));
            var dataMembers = userMeta.RowType.PersistentDataMembers;
            //var query = dataMembers.Where(d => d.IsPrimaryKey).Select(c=>c.Name).First();
            foreach (var element in dataMembers)
            {
                if (element.IsPrimaryKey)
                {
                    key = element.Name;
                    type = element.Type;
                    break;
                }
            }
            KeyValuePair<string, Type> kvp = new KeyValuePair<string, Type>(key, type);
            return kvp;

        }

        private string GetPrimaryKeyName<T>(T item)
        {
            var type = context.Mapping.GetMetaType(typeof(T));
            var PK = (from m in type.DataMembers
                      where m.IsPrimaryKey
                      select m).SingleOrDefault();
            if (PK != null)
                return PK.Name;
            else
                return null;

        }

        private object GetPrimaryKeyValue<T>(T item)
        {
            object value = null;
            string key = GetPrimaryKeyName<T>(item);
            if (key != null)
            {
                Type type = typeof(T);
                PropertyInfo pinfo = type.GetProperty(key);
                value = pinfo.GetValue(item, null);
            }
            return value;
        }

        private object GetValue<T>(T item, string propertyName) where T : class
        {
            object value = null;
            Type type = typeof(T);
            PropertyInfo pinfo = type.GetProperty(propertyName);
            value = pinfo.GetValue(item, null);
            return value;
        }

        private T Get<T>(object propertyValue) where T : class
        {

            KeyValuePair<string, Type> kvp = GetPrimaryKey<T>();
            Type propertyType = kvp.Value;
            string propertyName = kvp.Key;

            T result = null;

            ParameterExpression parameter = Expression.Parameter(typeof(T), "entity");

            Expression left = Expression.Property(parameter, propertyName);
            Expression right = Expression.Constant(propertyValue, propertyType);

            Expression predicateBody = Expression.Equal(left, right);
            var whereExpression = Expression.Lambda<Func<T, bool>>(predicateBody, new ParameterExpression[] { parameter });

            IQueryable<T> queryableData = context.GetTable<T>().AsQueryable<T>();
            if (queryableData != null)
                result = queryableData.Where(whereExpression).Single();
            return result;
        }

        private List<T> GetList<T>(string propertyName, object propertyValue) where T : class
        {
            List<T> list = new List<T>();
            ParameterExpression parameter = Expression.Parameter(typeof(T), "entity");
            Type type = typeof(T);
            PropertyInfo info = type.GetProperty(propertyName);
            Expression left = Expression.Property(parameter, propertyName);
            Expression right = Expression.Constant(propertyValue, info.PropertyType);

            Expression predicateBody = Expression.Equal(left, right);
            var whereExpression = Expression.Lambda<Func<T, bool>>(predicateBody, new ParameterExpression[] { parameter });

            IQueryable<T> queryableData = context.GetTable<T>().AsQueryable<T>();
            if (queryableData != null)
                list = queryableData.Where(whereExpression).ToList();
            return list;
        }

        private T SetValue<T>(T item, string propertyName, object propertyValue) where T : class
        {
            Type type = typeof(T);
            PropertyInfo pinfo = type.GetProperty(propertyName);
            pinfo.SetValue(item, propertyValue, null);
            return item;
        }

        public object Insert<T>(T item) where T : class {
            object value = null;
            context.GetTable<T>().InsertOnSubmit(item);
            context.SubmitChanges();
            string primaryKey = GetPrimaryKeyName(item);
            value = GetValue(item, primaryKey);
            return value;
        }

        public object Update<T>(T item) where T : class
        {
            object value = null;
            context.GetTable<T>().Attach(item);
            context.Refresh(RefreshMode.KeepCurrentValues, item);
            context.SubmitChanges();
            string primaryKey = GetPrimaryKeyName(item);
            value = GetValue(item, primaryKey);
            return value;
        }

        public void Update<T>(object id, string propertyName, object propertyValue) where T : class
        {
            T t = Get<T>(id);
            if (t != null)
            {
                t = SetValue<T>(t, propertyName, propertyValue);
                context.SubmitChanges();
            }
        }

        //public object InsertOrUpdate<T>(T item) where T: class {
        //    object value = null;
        //    bool flag = Exists(item);
        //    if (flag)
        //    {
        //        context.GetTable<T>().Attach(item);
        //        context.Refresh(RefreshMode.KeepCurrentValues, item);
        //    }
        //    else
        //        context.GetTable<T>().InsertOnSubmit(item);
        //    context.SubmitChanges();

        //    string primaryKey = GetPrimaryKeyName(item);
        //    value = GetValue(item, primaryKey);
        //    return value;
        //}

        public T InsertOrUpdate<T>(T item) where T : class
        {

            bool flag = Exists(item);
            if (flag)
            {
                context.GetTable<T>().Attach(item);
                context.Refresh(RefreshMode.KeepCurrentValues, item);
            }
            else
                context.GetTable<T>().InsertOnSubmit(item);
            context.SubmitChanges();
           
            return item;
        }


        private bool Exists<T>(T item) where T: class
        {
            using (TContext context = new TContext())
            {
                return context.GetTable<T>().Contains(item);
            }
        }       

        public void LoclkUnLock<T>(List<object> array_id, string propertyName) where T : class
        {
            List<T> list = new List<T>();
            foreach (var id in array_id)
            {
                T t = Get<T>(id);
                if (t != null)
                {
                    object value = GetValue(t, propertyName);
                    if (value != null)
                        value = ((bool)value) ? false : true;
                    else                    
                        value = true;
                    
                    t = SetValue<T>(t, propertyName, value);
                    list.Add(t);
                }
            }
            context.Refresh(RefreshMode.KeepCurrentValues, list);
            context.SubmitChanges();
        }

        public void InsertAllInOne<T>(List<object> valueFirst, string propertyNameFirst,string propertyNameSecond,object valueSecond) where T : class,new()
        {
            //Delete
            var entity = GetList<T>(propertyNameSecond, valueSecond);
            if (entity != null) {
                context.Refresh(RefreshMode.KeepCurrentValues, entity);
                context.GetTable<T>().DeleteAllOnSubmit(entity);
                context.SubmitChanges();
            }
            //Insert
            if (valueFirst != null)
            {
                List<T> list = new List<T>();
                foreach (var value in valueFirst)
                {
                    T t = new T();
                    t = SetValue<T>(t, propertyNameFirst, value);
                    t = SetValue<T>(t, propertyNameSecond, valueSecond);
                    list.Add(t);
                }
                context.GetTable<T>().InsertAllOnSubmit(list);
                context.SubmitChanges();
            }
        }

        public void Delete<T>(List<object> array_id) where T : class
        {
            List<T> list = new List<T>();
            foreach (var id in array_id)
            {
                T t = Get<T>(id);
                if (t != null)
                    list.Add(t);
            }
            context.GetTable<T>().DeleteAllOnSubmit(list);
            context.Refresh(RefreshMode.KeepCurrentValues, list);
            context.SubmitChanges();
        }

        public IList<object> List<T>(int? page, int? pageSize, Expression<Func<T, bool>> predicate, Expression<Func<T, object>> sort, Expression<Func<T, object>> selector) where T: class
        {
            using (TContext context = new TContext())
            {
                var result = context.GetTable<T>().AsQueryable();
                if (predicate != null)
                    result = result.Where(predicate);

                if (sort != null)
                    result = result.OrderBy(sort);

                if (page.HasValue && pageSize.HasValue)
                    result = result.Skip((page.Value - 1) * pageSize.Value).Take(pageSize.Value);

                if (selector != null)
                    return result.Select(selector).ToList<object>();
                else
                    return result.ToList<object>();
            }          
        }

        public IList<T> List<T>(int? page, int? pageSize, Expression<Func<T, bool>> predicate, Expression<Func<T, object>> sort) where T : class
        {
            using (TContext context = new TContext())
            {
                var result = context.GetTable<T>().AsQueryable();
                if (predicate != null)
                    result = result.Where(predicate);

                if (sort != null)
                    result = result.OrderBy(sort);

                if (page.HasValue && pageSize.HasValue)
                    result = result.Skip((page.Value - 1) * pageSize.Value).Take(pageSize.Value);
                return result.ToList<T>();
            }
        }

        public object  Get<T>(Expression<Func<T, bool>> predicate,Expression<Func<T, object>> selector) where T : class
        {
            using (TContext context = new TContext())
            {
                var result = context.GetTable<T>().AsQueryable();
                if (predicate != null)
                    result = result.Where(predicate);
                if (selector != null)
                    return result.Select(selector).SingleOrDefault<object>();
                else
                    return result.SingleOrDefault<object>();
            }
        }

        public T Get<T>(Expression<Func<T, bool>> predicate) where T : class
        {
            using (TContext context = new TContext())
            {
                var result = context.GetTable<T>().AsQueryable();
                if (predicate != null)
                    result = result.Where(predicate);
                return result.SingleOrDefault<T>();
            }
        }

        public void Dispose() {
            context.Dispose();        
        }
    }


}
